package com.fileupload.upload;

import com.fileupload.config.MonitorConfig;
import com.fileupload.config.ServerConfig;
import com.fileupload.exception.FileUploadException;
import com.fileupload.exception.RetryUtil;
import com.fileupload.server.Connection;
import com.fileupload.server.ConnectionManager;
import com.fileupload.server.ConnectionFactory;
import com.fileupload.process.FileProcessService;
import com.fileupload.upload.log.FileUploadLogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.UUID;
import java.text.SimpleDateFormat;

import com.fileupload.upload.strategy.ArchiveStrategy;
import com.fileupload.upload.strategy.ArchiveStrategyFactory;

/**
 * 文件上传服务实现类<br>
 * 负责将本地文件上传到远程服务器，提供文件上传、连接管理、异常处理和重试机制<br>
 * 该服务是系统的核心组件之一，与连接管理器、日志服务和重试工具协作完成文件上传任务
 * 
 * @author 小乙
 * @version 1.0
 * @since 2024-01-01
 */
@Service
public class FileUploadServiceImpl implements FileUploadService {
    /**
     * 日志记录器
     */
    private static final Logger logger = LoggerFactory.getLogger(FileUploadServiceImpl.class);
    
    /**
     * 连接管理器 - 负责管理与远程服务器的连接
     */
    @Autowired
    private ConnectionManager connectionManager;
    
    /**
     * 上传日志服务 - 记录文件上传的详细信息和状态
     */
    @Autowired
    private FileUploadLogService uploadLogService;
    
    @Autowired
    private ConnectionFactory connectionFactory;
    
    @Autowired
    private FileProcessService fileProcessService;
    
    /**
     * 上传文件到远程服务器
     * <p>该方法实现了文件上传的核心逻辑，包括参数校验、连接获取、重试机制、日志记录和上传后处理</p>
     * 
     * @param file 待上传的文件对象
     * @param monitorConfig 监控配置，包含服务器信息、重试参数等
     * @throws Exception 文件上传过程中发生的异常
     */
    @Override
    public void uploadFile(File file, MonitorConfig monitorConfig) throws Exception {
        // 记录进程开始
        logger.info("[文件上传进程] 开始处理文件上传请求");
        
        if (!file.exists() || !file.isFile()) {
            logger.error("[文件上传进程] 文件不存在或不是文件: {}", file.getAbsolutePath());
            throw new FileUploadException("FILE_NOT_EXIST", "文件不存在或不是文件: " + file.getAbsolutePath());
        }
        
        // 生成任务ID
        String taskId = UUID.randomUUID().toString();
        // 开始上传时间
        long startTime = System.currentTimeMillis();
        String serverName = monitorConfig.getServersPrimer();
        
        logger.info("[文件上传进程] 任务ID: {}, 待上传文件: {}, 监控配置: {}, 目标服务器组: {}", 
                taskId, file.getAbsolutePath(), monitorConfig.getName(), serverName);
        
        try {
            // 使用重试机制上传文件
            int maxRetries = monitorConfig.getMaxRetries() > 0 ? monitorConfig.getMaxRetries() : 3;
            long retryDelay = monitorConfig.getRetryDelay() > 0 ? monitorConfig.getRetryDelay() : 2000;
            
            logger.info("[文件上传进程] 配置重试参数: 最大重试次数={}, 重试延迟={}ms", maxRetries, retryDelay);
            
            RetryUtil.executeWithRetry(() -> {
                Connection connection = null;
                ServerConfig serverConfig = null;
                
                try {
                    // 根据监控配置获取服务器配置
                    logger.info("[文件上传进程] 获取服务器配置: {}", serverName);
                    serverConfig = connectionManager.getServerConfig(serverName);
                    
                    if (serverConfig == null) {
                        logger.error("[文件上传进程] 找不到服务器配置: {}", serverName);
                        throw new FileUploadException("SERVER_CONFIG_NOT_FOUND", "找不到服务器配置: " + serverName);
                    }
                    
                    logger.info("[文件上传进程] 服务器配置获取成功: 协议={}, 地址={}", 
                        serverConfig.getProtocol(), serverConfig.getAddr());
                    
                    // 获取连接
                    logger.info("[文件上传进程] 获取服务器连接: {}", serverName);
                    connection = connectionManager.getConnection(serverName);
                    
                    if (connection == null) {
                        logger.error("[文件上传进程] 无法获取服务器连接: {}", serverName);
                        throw new FileUploadException("CONNECTION_NOT_AVAILABLE", "无法获取服务器连接: " + serverName);
                    }
                    
                    // 连接到服务器
                    logger.info("[文件上传进程] 正在连接到服务器: {}://{}", 
                        serverConfig.getProtocol(), serverConfig.getAddr());
                    connection.connect();
                    logger.info("[文件上传进程] 服务器连接成功");
                    
                    Exception e = null; // 声明异常变量，用于finally块中检查
                    try {
                        // 准备上传路径
                        logger.info("[文件上传进程] 准备上传路径");
                        String remotePath = prepareRemotePath(serverConfig, monitorConfig, file);
                        
                        // 准备最终上传文件名
                        logger.info("[文件上传进程] 准备最终上传文件名");
                        String finalFileName = prepareUploadFileName(file, monitorConfig, serverConfig);
                        
                        // 准备临时上传文件名（如果配置了tmpSuffix）
                        String uploadFileName = finalFileName;
                        if (serverConfig != null && serverConfig.getTmpSuffix() != null && !serverConfig.getTmpSuffix().trim().isEmpty()) {
                            String tmpSuffix = serverConfig.getTmpSuffix().trim();
                            uploadFileName = finalFileName + "." + tmpSuffix;
                            logger.info("[文件上传进程] 使用临时文件后缀: {}，临时文件名为: {}", tmpSuffix, uploadFileName);
                        }
                        
                        // 上传文件
                        logger.info("[文件上传进程] 开始上传文件: {} 到服务器: {} 的路径: {}", 
                                file.getAbsolutePath(), serverName, remotePath + uploadFileName);
                        
                        boolean result = connection.uploadFile(file, remotePath, uploadFileName);
                        
                        if (!result) {
                            logger.error("[文件上传进程] 文件上传失败: {}", file.getAbsolutePath());
                            throw new FileUploadException("UPLOAD_FAILED", "文件上传失败: " + file.getAbsolutePath());
                        }
                        
                        // 如果使用了临时文件名，上传成功后重命名为最终文件名
                        if (!uploadFileName.equals(finalFileName)) {
                            logger.info("[文件上传进程] 开始重命名临时文件: {} -> {}", uploadFileName, finalFileName);
                            boolean renameResult = connection.renameFile(remotePath + uploadFileName, remotePath + finalFileName);
                            if (!renameResult) {
                                logger.error("[文件上传进程] 重命名临时文件失败: {} -> {}", uploadFileName, finalFileName);
                                throw new FileUploadException("RENAME_FAILED", "重命名临时文件失败: " + uploadFileName + " -> " + finalFileName);
                            }
                            logger.info("[文件上传进程] 临时文件重命名成功: {} -> {}", uploadFileName, finalFileName);
                        }
                        
                        long endTime = System.currentTimeMillis();
                        
                        // 记录成功日志
                        logger.info("[文件上传进程] 记录上传成功日志");
                        uploadLogService.logUploadResult(file, monitorConfig, serverConfig, startTime, endTime, true, null);
                        
                        logger.info("[文件上传进程] 文件上传成功: {}, 耗时: {}ms, 目标服务器: {}://{}:{}", 
                                file.getAbsolutePath(), (endTime - startTime), 
                        serverConfig.getProtocol(), serverConfig.getAddr());
                        
                        // 创建完成标记文件（根据server配置中的finishFlag决定）
                        ServerConfig serverConfigForFlag = connectionManager.getServerConfig(serverName);
                        if (serverConfigForFlag != null && serverConfigForFlag.getFinishFlag() != null && !serverConfigForFlag.getFinishFlag().isEmpty()) {
                            logger.info("[文件上传进程] 创建完成标记文件");
                            createFinishFlagFile(serverName, finalFileName, monitorConfig);
                            
                            // 处理本地完成标识文件
                            logger.info("[文件上传进程] 处理本地完成标识文件");
                            File localFlagFile = new File(file.getParentFile(), file.getName() + "." + serverConfigForFlag.getFinishFlag());
                            if (localFlagFile.exists()) {
                                processAfterUpload(localFlagFile, monitorConfig, true);
                            }
                        }
                        
                        // 处理文件（删除或移动）
                        logger.info("[文件上传进程] 执行上传后处理操作");
                        processAfterUpload(file, monitorConfig, true);
                        
                    } catch (Exception exception) {
                        e = exception; // 保存异常引用
                        throw exception;
                    } finally {
                        // 不再每次都断开连接，让ConnectionManager管理连接的生命周期
                        // 只在发生异常时断开连接，正常情况下保持连接以重用
                        if (e != null && connection != null && connection.isConnected()) {
                            try {
                                logger.info("[文件上传进程] 异常情况下关闭服务器连接");
                                connection.disconnect();
                                logger.info("[文件上传进程] 服务器连接已关闭");
                            } catch (Exception ex) {
                                logger.error("[文件上传进程] 关闭连接异常: {}", ex.getMessage(), ex);
                            }
                        }
                    }
                } catch (FileUploadException e) {
                    throw e;
                } catch (Exception e) {
                    throw new FileUploadException("UPLOAD_ERROR", "文件上传过程中发生错误", e);
                }
            }, maxRetries, retryDelay);
            
        } catch (FileUploadException e) {
            long endTime = System.currentTimeMillis();
            
            // 记录失败日志
            ServerConfig serverConfig = null;
            try {
                serverConfig = connectionManager.getServerConfig(serverName);
            } catch (Exception ex) {
                logger.error("获取服务器配置异常: {}", ex.getMessage(), ex);
            }
            
            uploadLogService.logUploadResult(file, monitorConfig, serverConfig, startTime, endTime, false, e.getMessage());
            
            logger.error("文件上传异常: [{}] {}", e.getErrorCode(), e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            
            // 记录失败日志
            ServerConfig serverConfig = null;
            try {
                serverConfig = connectionManager.getServerConfig(serverName);
            } catch (Exception ex) {
                logger.error("获取服务器配置异常: {}", ex.getMessage(), ex);
            }
            
            uploadLogService.logUploadResult(file, monitorConfig, serverConfig, startTime, endTime, false, e.getMessage());
            
            logger.error("文件上传异常: {}", e.getMessage(), e);
            throw new FileUploadException("UNKNOWN_ERROR", "文件上传过程中发生未知错误", e);
        }
    }
    
    /**
     * 上传成功后的处理
     * <p>根据配置执行不同的后续操作，包括删除源文件、移动源文件或创建完成标记文件</p>
     * 
     * @param file 已上传的文件
     * @param monitorConfig 监控配置，指定完成后执行的操作
     * @param success 上传是否成功
     */
    @Override
    public void processAfterUpload(File file, MonitorConfig monitorConfig, boolean success) {
        logger.info("[文件处理进程] 开始上传后文件处理: {}, 上传结果: {}", file.getAbsolutePath(), success ? "成功" : "失败");
        
        if (!success) {
            logger.info("[文件处理进程] 文件上传失败，不进行后续处理: {}", file.getAbsolutePath());
            return;
        }
        
        String finishAct = monitorConfig.getFinishAct();
        try {
            logger.info("[文件处理进程] 开始处理上传后的文件: {}, 动作: {}, 监控配置: {}", file.getAbsolutePath(), finishAct, monitorConfig.getName());
            // 处理原始上传文件
            fileProcessService.processFile(file, finishAct, monitorConfig.getMoveTo(), monitorConfig.getMonitorDir());
            
            // 检查是否配置了monitorFileSuffix，如果有，也处理对应的标记文件
            String monitorFileSuffix = monitorConfig.getMonitorFileSuffix();
            if (monitorFileSuffix != null && !monitorFileSuffix.trim().isEmpty()) {
                logger.info("[文件处理进程] 检查是否存在对应的标记文件，后缀: {}", monitorFileSuffix);
                
                // 生成标记文件名（例如：1.txt -> 1.ok）
                String baseName = file.getName();
                int dotIndex = baseName.lastIndexOf('.');
                if (dotIndex > 0) {
                    baseName = baseName.substring(0, dotIndex);
                }
                String flagFileName = baseName + "." + monitorFileSuffix;
                File flagFile = new File(file.getParentFile(), flagFileName);
                
                // 如果标记文件存在，按照相同的finishAct处理它
                if (flagFile.exists()) {
                    logger.info("[文件处理进程] 发现标记文件: {}，开始按照相同的动作处理", flagFile.getAbsolutePath());
                    fileProcessService.processFile(flagFile, finishAct, monitorConfig.getMoveTo(), monitorConfig.getMonitorDir());
                    logger.info("[文件处理进程] 标记文件处理完成: {}", flagFile.getAbsolutePath());
                } else {
                    logger.info("[文件处理进程] 标记文件不存在，跳过处理: {}", flagFile.getAbsolutePath());
                }
            }
            
            logger.info("[文件处理进程] 上传后文件处理完成: {}", file.getAbsolutePath());
        } catch (Exception e) {
            logger.error("[文件处理进程] 处理上传后的文件异常: {}, 文件: {}, 异常: {}", finishAct, file.getAbsolutePath(), e.getMessage(), e);
            // 这里不抛出异常，避免影响主流程
        }
    }
    
    /**
     * 创建完成标记文件
     * <p>在远程服务器上创建一个标记文件，表示上传任务已完成</p>
     * 
     * @param serverId 服务器ID
     * @param fileName 已上传的文件名
     * @param monitorConfig 监控配置，包含标记文件相关设置
     * @throws Exception 创建标记文件过程中发生的异常
     */
    @Override
    public void createFinishFlagFile(String serverId, String fileName, MonitorConfig monitorConfig) throws Exception {
        Connection connection = null;
        try {
            // 检查必要参数
            if (serverId == null || serverId.isEmpty()) {
                throw new IllegalArgumentException("服务器ID不能为空");
            }
            
            if (fileName == null || fileName.isEmpty()) {
                throw new IllegalArgumentException("文件名不能为空");
            }
            
            if (monitorConfig == null) {
                throw new IllegalArgumentException("监控配置不能为空");
            }
            
            // 获取服务器配置和连接
            ServerConfig serverConfig = connectionManager.getServerConfig(serverId);
            if (serverConfig == null) {
                throw new FileUploadException("SERVER_CONFIG_NOT_FOUND", "找不到服务器配置: " + serverId);
            }
            
            // 从ServerConfig获取finishFlag
            String finishFlag = serverConfig.getFinishFlag();
            if (finishFlag == null || finishFlag.isEmpty()) {
                logger.warn("服务器[{}]未配置finishFlag，跳过创建标记文件", serverId);
                return;
            }
            
            connection = connectionManager.getConnection(serverId);
            if (connection == null) {
                throw new FileUploadException("CONNECTION_NOT_AVAILABLE", "无法获取服务器连接: " + serverId);
            }
            
            // 确保连接已建立
            if (!connection.isConnected()) {
                connection.connect();
            }
            
            // 准备路径和文件名
            // 创建临时File对象，仅用于构建路径
            File tempFile = new File(fileName);
            String remotePath = prepareRemotePath(serverConfig, monitorConfig, tempFile);
            
            // 生成标记文件名（例如：1.txt -> 1.ok）
            String baseName = fileName;
            int dotIndex = fileName.lastIndexOf('.');
            if (dotIndex > 0) {
                baseName = fileName.substring(0, dotIndex);
            }
            String flagFileName = baseName + "." + finishFlag;
            
            // 获取标记文件内容
            String flagContent = serverConfig.getFinishFlagContent();
            if (flagContent == null || flagContent.isEmpty()) {
                flagContent = "File upload completed";
            }
            
            // 创建标记文件
            String fullFlagPath = remotePath + flagFileName;
            boolean result = connection.writeTextFile(fullFlagPath, flagContent);
            
            if (result) {
                logger.info("创建完成标记文件成功: {}{}", remotePath, flagFileName);
            } else {
                logger.error("创建完成标记文件失败: {}{}", remotePath, flagFileName);
                throw new FileUploadException("FLAG_FILE_CREATE_FAILED", "创建完成标记文件失败: " + flagFileName);
            }
        } catch (Exception exception) {
            logger.error("创建完成标记文件异常: {}", exception.getMessage(), exception);
            throw exception;
        } finally {
            // 不再每次都断开连接，让ConnectionManager管理连接的生命周期
            // 只在发生异常时断开连接，正常情况下保持连接以重用
            // 注意：这里不再检查异常状态，总是保持连接打开以重用
            // if (e != null && connection != null && connection.isConnected()) {
            //     try {
            //         connection.disconnect();
            //     } catch (Exception ex) {
            //         logger.error("关闭连接异常: {}", ex.getMessage());
            //     }
            // }
        }
    }
    
    /**
     * 准备远程路径
     * 使用策略模式处理不同的归档方式
     */
    private String prepareRemotePath(ServerConfig serverConfig, MonitorConfig monitorConfig, File file) {
        String baseDir = serverConfig.getDir();
        String archive = serverConfig.getArchive();
        
        StringBuilder pathBuilder = new StringBuilder();
        
        // 添加基础目录
        if (baseDir != null && !baseDir.isEmpty()) {
            pathBuilder.append(baseDir);
            if (!baseDir.endsWith("/")) {
                pathBuilder.append("/");
            }
        }
        
        // 使用策略模式处理归档目录
        if (archive != null && !archive.isEmpty()) {
            // 从工厂获取对应的归档策略
            ArchiveStrategy archiveStrategy = ArchiveStrategyFactory.getArchiveStrategy(archive);
            
            // 构建归档路径
            String monitorDir = monitorConfig != null ? monitorConfig.getMonitorDir() : null;
            String archivePath = archiveStrategy.buildArchivePath(monitorDir, file);
            
            // 添加归档路径
            if (archivePath != null && !archivePath.isEmpty()) {
                pathBuilder.append(archivePath);
            }
        }
        
        String finalPath = pathBuilder.toString();
        logger.info("[文件上传进程] 最终构建的远程路径: {}", finalPath);
        return finalPath;
    }
    
    /**
     * 准备上传文件名
     * 优先使用ServerConfig中的fileSuffix，其次使用MonitorConfig中的uploadFileSuffix
     */
    protected String prepareUploadFileName(File file, MonitorConfig monitorConfig, ServerConfig serverConfig) {
        String originalName = file.getName();
        
        // 首先检查ServerConfig中的fileSuffix（优先级更高）
        if (serverConfig != null && serverConfig.getFileSuffix() != null && !serverConfig.getFileSuffix().trim().isEmpty()) {
            String fileSuffix = serverConfig.getFileSuffix().trim();
            logger.info("[文件上传进程] 使用服务器配置的文件后缀: {}", fileSuffix);
            
            // 替换文件后缀
            int dotIndex = originalName.lastIndexOf('.');
            if (dotIndex > 0) {
                return originalName.substring(0, dotIndex) + "." + fileSuffix;
            } else {
                return originalName + "." + fileSuffix;
            }
        }
        
        // 如果ServerConfig中没有配置，则使用MonitorConfig中的uploadFileSuffix
        String uploadSuffix = monitorConfig.getUploadFileSuffix();
        if (uploadSuffix != null && !uploadSuffix.isEmpty()) {
            logger.info("[文件上传进程] 使用监控配置的文件后缀: {}", uploadSuffix);
            
            // 替换文件后缀
            int dotIndex = originalName.lastIndexOf('.');
            if (dotIndex > 0) {
                return originalName.substring(0, dotIndex) + "." + uploadSuffix;
            } else {
                return originalName + "." + uploadSuffix;
            }
        }
        
        logger.info("[文件上传进程] 未配置文件后缀，使用原始文件名");
        return originalName;
    }
    
    /**
     * 准备标记文件内容
     */
    private String prepareFlagContent(String contentTemplate) {
        if (contentTemplate == null || contentTemplate.isEmpty()) {
            return "";
        }
        
        if (contentTemplate.equalsIgnoreCase("date")) {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        }
        
        return contentTemplate;
    }
    
    /**
     * 删除文件
     */
    private void deleteFile(File file) {
        try {
            if (file.delete()) {
                logger.info("文件删除成功: {}", file.getAbsolutePath());
            } else {
                logger.warn("文件删除失败: {}", file.getAbsolutePath());
            }
        } catch (Exception e) {
            logger.error("删除文件异常: {}", e.getMessage());
        }
    }
    
    /**
     * 移动文件
     */
    private void moveFile(File file, String targetDir) {
        try {
            if (targetDir == null || targetDir.isEmpty()) {
                logger.warn("目标移动目录为空，无法移动文件: {}", file.getAbsolutePath());
                return;
            }
            
            Path targetPath = Paths.get(targetDir, file.getName());
            Path targetDirPath = targetPath.getParent();
            
            // 创建目标目录
            if (!Files.exists(targetDirPath)) {
                Files.createDirectories(targetDirPath);
                logger.info("创建目标目录: {}", targetDirPath);
            }
            
            // 移动文件
            Files.move(file.toPath(), targetPath);
            logger.info("文件移动成功: {} -> {}", file.getAbsolutePath(), targetPath);
        } catch (Exception e) {
            logger.error("移动文件异常: {}", e.getMessage());
        }
    }
}